package mornd.test1;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author: mornd
 * @dateTime: 2023/5/5 - 16:49
 */

@ComponentScan("mornd/test1")
public class App {

    /**
     * 单例 bean 中，依赖注入多例 bean 时，需添加 @Lazy 注解
     * 对于单例对象A来说，依赖注入仅发生了一次，后续不会再进行注入B，对象已经确定，后续始终用第一次注入的对象
     * 添加完@Lazy 注解后，代理对象A还是同一个，对于多例B，每当使用代理对象A的任意方法时，代理对象会创建新的B对象
     * @param args
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App.class);
        A a = context.getBean(A.class);
        B b = a.getB();
        B b2 = a.getB();
        System.out.println(b.getClass());
        System.out.println(b);
        System.out.println(b2);
        System.out.println(b == b2);

        System.out.println("----------------------------------");

        C c = a.getC();
        C c2 = a.getC();
        System.out.println(c.getClass());
        System.out.println(c);
        System.out.println(c2);
        System.out.println(c == c2);

        System.out.println("----------------------------------");

        D d = a.getD();
        D d2 = a.getD();
        System.out.println(d.getClass());
        System.out.println(d);
        System.out.println(d2);
        System.out.println(d == d2);

        System.out.println("----------------------------------");

        E e = a.getE();
        E e2 = a.getE();
        System.out.println(e.getClass());
        System.out.println(e);
        System.out.println(e2);
        System.out.println(e == e2);
    }


    @Component
    static class A {

        // 方式1，属性加注解，生成 B 的代理对象
        @Lazy
        @Autowired
        private B b;

        @Autowired
        private C c;

        // 方式3，通过 ObjectFactory 生成多例对象，但工厂生成的对象不是代理对象
        @Autowired
        private ObjectFactory<D> d;

        public B getB() {
            return b;
        }

        public C getC() {
            return c;
        }

        public D getD() {
            return d.getObject();
        }

        // 方式4，通过容器获取多例 bean，也不会生成代理对象
        @Autowired
        private ApplicationContext applicationContext;

        public E getE() {
            return applicationContext.getBean(E.class);
        }
    }

    @Scope("prototype")
    @Component
    static class B {}

    // 方式2，添加 proxyMode 属性
    @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Component
    static class C {}

    @Scope("prototype")
    @Component
    static class D {}

    @Scope("prototype")
    @Component
    static class E {}
}
